#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>

#include "sample_comm.h"

#include "od_def.h"
#include "od_trace.h"
#include "od_task.h"
#include "od_tube.h"
#include "od_os.h"
#include "od_msg.h"
#include "od_os_config.h"
#include "od_engine.h"
#include "HisiDecVpssOut.h"

#define HISI_DEC_VPSS_OUT_TSK_STACK_SIZE    (4*1024 * 1024)   /*线程栈资源大小*/

#define HIOUT_ASSERT_RETURN(expr,retval)    \
        do { \
        if (!(expr)) { assert(expr); return retval; } \
        } while (0)

#define HIOUT_SIP_ASSERT(expr)    \
        do { \
        if (!(expr)) { assert(expr);} \
        } while (0)    


typedef struct
{
    TASK_COMMON_MEMBERS

    bool sysBindDone;
    bool chnCfgDone;
    struct //HiMPP-API core parameter config!
    {
        SIZE_S stSize;
        HI_U32 u32ChnCnt;
        VO_DEV VoDev;
        VO_LAYER VoLayer;
        VPSS_CHN VpssChn;
        VDEC_CHN_ATTR_S stVdecChnAttr[VDEC_MAX_CHN_NUM];
        VPSS_GRP_ATTR_S stVpssGrpAttr[VDEC_MAX_CHN_NUM];
        VO_PUB_ATTR_S stVoPubAttr;
        VO_VIDEO_LAYER_ATTR_S stVoLayerAttr;
    }himpp_cfg;
}hisiDecVpssOut_obj;


params_def hisiDecVpssOut_dynamic_params_def[]=
{
    {OFFSET(hisiDecVpssOut_dynamic_params, enType),   PT_VAR_INT32 | PT_REAL_TIME, sizeof(int), PT_PCMU, PT_BUTT},
    {OFFSET(hisiDecVpssOut_dynamic_params, voMode),   PT_VAR_INT32 | PT_REAL_TIME , sizeof(int), VO_MODE_1MUX, VO_MODE_BUTT},     
    {OFFSET(hisiDecVpssOut_dynamic_params, voFormat), PT_VAR_INT32 | PT_REAL_TIME , sizeof(int), VO_OUTPUT_PAL, VO_OUTPUT_BUTT},  
    {0, 0, 0, 0, 0}
};

const hisiDecVpssOut_static_params glb_hisiDecVpssOut_static_params_default = 
{
    "hisi-vpss-out"
};

const hisiDecVpssOut_dynamic_params glb_hisiDecVpssOut_dynamic_params_default = 
{
    PT_H264,             /*enType*/
    VO_MODE_9MUX,        /*voMode*/
    VO_OUTPUT_1080P25    /*voFormat*/
};

static void hisiDecVpssOut_default(hisiDecVpssOut_obj *obj)
{
    obj->himpp_cfg.stSize.u32Width  = HD_WIDTH;
    obj->himpp_cfg.stSize.u32Height = HD_HEIGHT;
    obj->himpp_cfg.u32ChnCnt = 9;   //FIXME: 目前最高支持9路
    obj->himpp_cfg.VoDev = SAMPLE_VO_DEV_DHD0;
    obj->himpp_cfg.VoLayer = SAMPLE_VO_LAYER_VHD0;
    obj->himpp_cfg.VpssChn = VPSS_CHN0;
    obj->sysBindDone = false;
    obj->chnCfgDone = false;
}

static int hisiDecVpssOut_start_stop(hisiDecVpssOut_obj *obj)
{
    hisiDecVpssOut_dynamic_params *param = obj->params;
    HI_S32 s32Ret = HI_SUCCESS;
    VB_CONF_S stVbConf, stModVbConf;

    /*init cfg params*/
    hisiDecVpssOut_default(obj);
    
    /************************************************
    step1:  init SYS and common VB 
    *************************************************/
    SAMPLE_COMM_VDEC_Sysconf(&stVbConf, &obj->himpp_cfg.stSize);
    s32Ret = SAMPLE_COMM_SYS_Init(&stVbConf);
    if(s32Ret != HI_SUCCESS)
    {
        RPTERR("init sys fail for %#x!\n", s32Ret);
        goto END1;
    }

    /************************************************
    step2:  init mod common VB
    *************************************************/
    SAMPLE_COMM_VDEC_ModCommPoolConf(&stModVbConf, param->enType,
                                     &obj->himpp_cfg.stSize,
                                     obj->himpp_cfg.u32ChnCnt);	
    s32Ret = SAMPLE_COMM_VDEC_InitModCommVb(&stModVbConf);
    if(s32Ret != HI_SUCCESS)
    {
        RPTERR("init mod common vb fail for %#x!\n", s32Ret);
        goto END1;
    }

    /************************************************
    step3:  start VDEC
    *************************************************/
    SAMPLE_COMM_VDEC_ChnAttr(obj->himpp_cfg.u32ChnCnt, 
                            &obj->himpp_cfg.stVdecChnAttr[0], 
                            param->enType,
                            &obj->himpp_cfg.stSize);
    s32Ret = SAMPLE_COMM_VDEC_Start(obj->himpp_cfg.u32ChnCnt, 
                                    &obj->himpp_cfg.stVdecChnAttr[0]);
    if(s32Ret != HI_SUCCESS)
    {
        RPTERR("start VDEC fail for %#x!\n", s32Ret);
        goto END2;
    }

    /************************************************
    step4:  start VPSS
    *************************************************/
    SAMPLE_COMM_VDEC_VpssGrpAttr(obj->himpp_cfg.u32ChnCnt,
                                &obj->himpp_cfg.stVpssGrpAttr[0], 
                                &obj->himpp_cfg.stSize);
    s32Ret = SAMPLE_COMM_VPSS_Start(obj->himpp_cfg.u32ChnCnt, 
                                    &obj->himpp_cfg.stSize, 
                                    1, 
                                    &obj->himpp_cfg.stVpssGrpAttr[0]);
    if(s32Ret != HI_SUCCESS)
    {	    
        RPTERR("start VPSS fail for %#x!\n", s32Ret);
        goto END3;
    }

    return OD_OK;

END3:
    SAMPLE_COMM_VPSS_Stop(obj->himpp_cfg.u32ChnCnt, obj->himpp_cfg.VpssChn);
END2:
    SAMPLE_COMM_VDEC_Stop(obj->himpp_cfg.u32ChnCnt);    
END1:
    SAMPLE_COMM_SYS_Exit();	
    
    return OD_ERROR;
}

static void hisiDecVpssOut_start_stop_delete(hisiDecVpssOut_obj *obj)
{   
    HI_S32 s32Ret = HI_SUCCESS;
    int i;
    
    /*FIXME: may be failed!!*/
    for(i=0; i<obj->himpp_cfg.u32ChnCnt; i++)
    {
        s32Ret = SAMPLE_COMM_VO_UnBindVpss(obj->himpp_cfg.VoLayer, i, i, obj->himpp_cfg.VpssChn);
        if(s32Ret != HI_SUCCESS)
        {	    
            RPTERR("vpss unbind vo fail for %#x!\n", s32Ret);
        }	
    }

    for(i=0; i<obj->himpp_cfg.u32ChnCnt; i++)
    {
        s32Ret = SAMPLE_COMM_VDEC_UnBindVpss(i, i);
        if(s32Ret != HI_SUCCESS)
        {	    
            RPTERR("vdec unbind vpss fail for %#x!\n", s32Ret);
        }	
    }
    
    SAMPLE_COMM_VPSS_Stop(obj->himpp_cfg.u32ChnCnt, obj->himpp_cfg.VpssChn);
    SAMPLE_COMM_VDEC_Stop(obj->himpp_cfg.u32ChnCnt);
    SAMPLE_COMM_SYS_Exit();	
}

static int hisiDecVpssOut_malloc_free(hisiDecVpssOut_obj *obj)
{
    hisiDecVpssOut_dynamic_params *param = obj->params;
    HI_S32 s32Ret = HI_SUCCESS;
    int i;
    
    /************************************************
    step5:  start VO
    *************************************************/      
    obj->himpp_cfg.stVoPubAttr.enIntfSync = param->voFormat;
    obj->himpp_cfg.stVoPubAttr.enIntfType = VO_INTF_HDMI | VO_INTF_VGA;

    s32Ret = SAMPLE_COMM_VO_StartDev(obj->himpp_cfg.VoDev, &obj->himpp_cfg.stVoPubAttr);
    if(s32Ret != HI_SUCCESS)
    {       
        RPTERR("vdec bind vpss fail for %#x!\n", s32Ret);
        goto END4_1;
    }

    if (HI_SUCCESS != SAMPLE_COMM_VO_HdmiStart(obj->himpp_cfg.stVoPubAttr.enIntfSync))
    {
        RPTERR("Start SAMPLE_COMM_VO_HdmiStart failed!\n");
        goto END4_2;
    }

    /*FIXME: need VGA-out config!!*/

    obj->himpp_cfg.stVoLayerAttr.bClusterMode = HI_FALSE;
    obj->himpp_cfg.stVoLayerAttr.bDoubleFrame = HI_FALSE;
    obj->himpp_cfg.stVoLayerAttr.enPixFormat = SAMPLE_PIXEL_FORMAT;    

    s32Ret = SAMPLE_COMM_VO_GetWH(obj->himpp_cfg.stVoPubAttr.enIntfSync, \
                                  &obj->himpp_cfg.stVoLayerAttr.stDispRect.u32Width, 
                                  &obj->himpp_cfg.stVoLayerAttr.stDispRect.u32Height, 
                                  &obj->himpp_cfg.stVoLayerAttr.u32DispFrmRt);
    if (s32Ret != HI_SUCCESS)
    {
        RPTERR("failed with %#x!\n", s32Ret);
        goto  END4_2;
    }
    
    obj->himpp_cfg.stVoLayerAttr.stImageSize.u32Width = obj->himpp_cfg.stVoLayerAttr.stDispRect.u32Width;
    obj->himpp_cfg.stVoLayerAttr.stImageSize.u32Height = obj->himpp_cfg.stVoLayerAttr.stDispRect.u32Height;
    s32Ret = SAMPLE_COMM_VO_StartLayer(obj->himpp_cfg.VoLayer, &obj->himpp_cfg.stVoLayerAttr);
    if(s32Ret != HI_SUCCESS)
    {       
        RPTERR("SAMPLE_COMM_VO_StartLayer fail for %#x!\n", s32Ret);
        goto END4_3;
    }   

    if (obj->sysBindDone == false) {
        /************************************************
        step6:  VDEC bind VPSS
        *************************************************/  
        for(i=0; i<obj->himpp_cfg.u32ChnCnt; i++)
        {
            s32Ret = SAMPLE_COMM_VDEC_BindVpss(i, i);
            if(s32Ret != HI_SUCCESS)
            {       
                RPTERR("vdec bind vpss fail for %#x!\n", s32Ret);
                goto END5;
            }   
        }
            
        /************************************************
        step7:  VPSS bind VO
        *************************************************/
        for(i=0; i<obj->himpp_cfg.u32ChnCnt; i++)
        {
            s32Ret = SAMPLE_COMM_VO_BindVpss(obj->himpp_cfg.VoLayer, i, i, obj->himpp_cfg.VpssChn);
            if(s32Ret != HI_SUCCESS)
            {       
                RPTERR("vpss bind vo fail for %#x!\n", s32Ret);
                goto END6;
            }   
        }

        obj->sysBindDone = true;
    }
    
    return OD_OK;


END6:
    for(i=0; i<obj->himpp_cfg.u32ChnCnt; i++)
    {
        s32Ret = SAMPLE_COMM_VO_UnBindVpss(obj->himpp_cfg.VoLayer, i, i, obj->himpp_cfg.VpssChn);
        if(s32Ret != HI_SUCCESS)
        {	    
            RPTERR("vpss unbind vo fail for %#x!\n", s32Ret);
        }	
    }

END5:
    for(i=0; i<obj->himpp_cfg.u32ChnCnt; i++)
    {
        s32Ret = SAMPLE_COMM_VDEC_UnBindVpss(i, i);
        if(s32Ret != HI_SUCCESS)
        {	    
            RPTERR("vdec unbind vpss fail for %#x!\n", s32Ret);
        }	
    }
        
END4_3: 
    SAMPLE_COMM_VO_StopLayer(obj->himpp_cfg.VoLayer);    
END4_2: 
    SAMPLE_COMM_VO_HdmiStop();
END4_1:
    SAMPLE_COMM_VO_StopDev(obj->himpp_cfg.VoDev);

    return OD_ERROR;
}

static void hisiDecVpssOut_malloc_free_delete(hisiDecVpssOut_obj *obj)
{
    hisiDecVpssOut_dynamic_params *param = obj->params;
    
    SAMPLE_COMM_VO_StopChn(obj->himpp_cfg.VoLayer, param->voMode);	
    SAMPLE_COMM_VO_StopLayer(obj->himpp_cfg.VoLayer);
    SAMPLE_COMM_VO_HdmiStop();
    SAMPLE_COMM_VO_StopDev(obj->himpp_cfg.VoDev);
}

static int hisiDecVpssOut_ret_set(hisiDecVpssOut_obj *obj)
{
    hisiDecVpssOut_dynamic_params *param = obj->params;
    HI_S32 s32Ret = HI_SUCCESS;

    if (obj->chnCfgDone == true)
        SAMPLE_COMM_VO_StopChn(obj->himpp_cfg.VoLayer, param->voMode); 
    
    s32Ret = SAMPLE_COMM_VO_StartChn(obj->himpp_cfg.VoLayer, param->voMode);
    if(s32Ret != HI_SUCCESS)
    {		
        RPTERR("vdec bind vpss fail for %#x!\n", s32Ret);
        goto END4_4;
    }

    if(0) { //test code
        char *pStreamFileName = SAMPLE_1080P_H264_PATH;
        VdecThreadParam stVdecSend[VDEC_MAX_CHN_NUM];
        pthread_t   VdecThread[2*VDEC_MAX_CHN_NUM];
        
        /************************************************
        step8:  send stream to VDEC
        *************************************************/
        SAMPLE_COMM_VDEC_ThreadParam(obj->himpp_cfg.u32ChnCnt, 
                                    &stVdecSend[0],
                                    &obj->himpp_cfg.stVdecChnAttr[0],
                                    pStreamFileName);	
        
        SAMPLE_COMM_VDEC_StartSendStream(obj->himpp_cfg.u32ChnCnt, 
                                        &stVdecSend[0], 
                                        &VdecThread[0]);
    }


    obj->chnCfgDone = true;
    return OD_OK;
    
END4_4:    
    SAMPLE_COMM_VO_StopChn(obj->himpp_cfg.VoLayer, param->voMode);  

    return OD_ERROR;
}

static int hisiDecVpssOut_process(hisiDecVpssOut_obj *obj)
{
    RPTDBG("run loop!");
    od_msleep(5000);
    return OD_OK;
}

hisiDecVpssOut_handle hisiDecVpssOut_create(hisiDecVpssOut_static_params *static_params,
                                              hisiDecVpssOut_dynamic_params *dynamic_params)
{
    hisiDecVpssOut_obj *obj = NULL;
    hisiDecVpssOut_dynamic_params *params = NULL;

    HIOUT_ASSERT_RETURN(static_params, NULL);

    obj = od_task_create((char *)static_params->task_name, sizeof(hisiDecVpssOut_obj));
    HIOUT_ASSERT_RETURN(obj, NULL);

    obj->tab = hisiDecVpssOut_dynamic_params_def;
    obj->params_nums = sizeof(hisiDecVpssOut_dynamic_params_def) / sizeof(params_def) - 1;

    params = od_calloc(1, sizeof(hisiDecVpssOut_dynamic_params));
    HIOUT_ASSERT_RETURN(params, NULL);

    if (NULL == dynamic_params)
        memcpy(params, &glb_hisiDecVpssOut_dynamic_params_default, sizeof(hisiDecVpssOut_dynamic_params));
    else
        memcpy(params, dynamic_params, sizeof(hisiDecVpssOut_dynamic_params));

    obj->params = (void *)params;
    obj->stack_size = HISI_DEC_VPSS_OUT_TSK_STACK_SIZE;
    obj->pf_start_stop  = (void *)hisiDecVpssOut_start_stop;
    obj->pf_malloc_free = (void *)hisiDecVpssOut_malloc_free;
    obj->pf_rt_set = (void *)hisiDecVpssOut_ret_set;
    obj->pf_process     = (void *)hisiDecVpssOut_process;
    obj->pf_malloc_free_delete = (void *)hisiDecVpssOut_malloc_free_delete;
    obj->pf_start_stop_delete = (void *)hisiDecVpssOut_start_stop_delete;
    
    RPTDBG("create success!");
    return (hisiDecVpssOut_handle)obj;
}

/*_*/

